/*
Syntax highlighting with language autodetection.
https://highlightjs.org/
 */

(function(factory) {

	// Setup highlight.js for different environments. First is Node.js or
	// CommonJS.
	if (typeof exports !== 'undefined') {
		factory(exports);
	} else {
		// Export hljs globally even when using AMD for cases when this script
		// is loaded with others that may still expect a global hljs.
		window.hljs = factory({});

		// Finally register the global hljs with AMD.
		if (typeof define === 'function' && define.amd) {
			define('hljs', [], function() {
				return window.hljs;
			});
		}
	}

}
		(function(hljs) {

			/* Utility functions */

			function escape(value) {
				return value.replace(/&/gm, '&amp;').replace(/</gm, '&lt;').replace(/>/gm, '&gt;');
			}

			function tag(node) {
				return node.nodeName.toLowerCase();
			}

			function testRe(re, lexeme) {
				var match = re && re.exec(lexeme);
				return match && match.index == 0;
			}

			function isNotHighlighted(language) {
				return (/^(no-?highlight|plain|text)$/i).test(language);
			}

			function blockLanguage(block) {
				var i, match, length, classes = block.className + ' ';

				classes += block.parentNode ? block.parentNode.className : '';

				// language-* takes precedence over non-prefixed class names
				match = (/\blang(?:uage)?-([\w-]+)\b/i).exec(classes);
				if (match) {
					return getLanguage(match[1]) ? match[1] : 'no-highlight';
				}

				classes = classes.split(/\s+/);
				for (i = 0, length = classes.length; i < length; i++) {
					if (getLanguage(classes[i]) || isNotHighlighted(classes[i])) {
						return classes[i];
					}
				}
			}

			function inherit(parent, obj) {
				var result = {}, key;
				for (key in parent)
					result[key] = parent[key];
				if (obj)
					for (key in obj)
						result[key] = obj[key];
				return result;
			}

			/* Stream merging */

			function nodeStream(node) {
				var result = [];
				(function _nodeStream(node, offset) {
					for (var child = node.firstChild; child; child = child.nextSibling) {
						if (child.nodeType == 3)
							offset += child.nodeValue.length;
						else if (child.nodeType == 1) {
							result.push({
								event : 'start',
								offset : offset,
								node : child
							});
							offset = _nodeStream(child, offset);
							// Prevent void elements from having an end tag that
							// would actually
							// double them in the output. There are more void
							// elements in HTML
							// but we list only those realistically expected in
							// code display.
							if (!tag(child).match(/br|hr|img|input/)) {
								result.push({
									event : 'stop',
									offset : offset,
									node : child
								});
							}
						}
					}
					return offset;
				})(node, 0);
				return result;
			}

			function mergeStreams(original, highlighted, value) {
				var processed = 0;
				var result = '';
				var nodeStack = [];

				function selectStream() {
					if (!original.length || !highlighted.length) {
						return original.length ? original : highlighted;
					}
					if (original[0].offset != highlighted[0].offset) {
						return (original[0].offset < highlighted[0].offset) ? original : highlighted;
					}

					/*
					 * To avoid starting the stream just before it should stop
					 * the order is ensured that original always starts first
					 * and closes last:
					 * 
					 * if (event1 == 'start' && event2 == 'start') return
					 * original; if (event1 == 'start' && event2 == 'stop')
					 * return highlighted; if (event1 == 'stop' && event2 ==
					 * 'start') return original; if (event1 == 'stop' && event2 ==
					 * 'stop') return highlighted;
					 * 
					 * ... which is collapsed to:
					 */
					return highlighted[0].event == 'start' ? original : highlighted;
				}

				function open(node) {
					function attr_str(a) {
						return ' ' + a.nodeName + '="' + escape(a.value) + '"';
					}
					result +=
							'<' + tag(node) + Array.prototype.map.call(node.attributes, attr_str).join('')
									+ '>';
				}

				function close(node) {
					result += '</' + tag(node) + '>';
				}

				function render(event) {
					(event.event == 'start' ? open : close)(event.node);
				}

				while (original.length || highlighted.length) {
					var stream = selectStream();
					result += escape(value.substr(processed, stream[0].offset - processed));
					processed = stream[0].offset;
					if (stream == original) {
						/*
						 * On any opening or closing tag of the original markup
						 * we first close the entire highlighted node stack,
						 * then render the original tag along with all the
						 * following original tags at the same offset and then
						 * reopen all the tags on the highlighted stack.
						 */
						nodeStack.reverse().forEach(close);
						do {
							render(stream.splice(0, 1)[0]);
							stream = selectStream();
						} while (stream == original && stream.length && stream[0].offset == processed);
						nodeStack.reverse().forEach(open);
					} else {
						if (stream[0].event == 'start') {
							nodeStack.push(stream[0].node);
						} else {
							nodeStack.pop();
						}
						render(stream.splice(0, 1)[0]);
					}
				}
				return result + escape(value.substr(processed));
			}

			/* Initialization */

			function compileLanguage(language) {

				function reStr(re) {
					return (re && re.source) || re;
				}

				function langRe(value, global) {
					return new RegExp(reStr(value), 'm' + (language.case_insensitive ? 'i' : '')
							+ (global ? 'g' : ''));
				}

				function compileMode(mode, parent) {
					if (mode.compiled)
						return;
					mode.compiled = true;

					mode.keywords = mode.keywords || mode.beginKeywords;
					if (mode.keywords) {
						var compiled_keywords = {};

						var flatten = function(className, str) {
							if (language.case_insensitive) {
								str = str.toLowerCase();
							}
							str.split(' ').forEach(function(kw) {
								var pair = kw.split('|');
								compiled_keywords[pair[0]] = [ className, pair[1] ? Number(pair[1]) : 1 ];
							});
						};

						if (typeof mode.keywords == 'string') { // string
							flatten('keyword', mode.keywords);
						} else {
							Object.keys(mode.keywords).forEach(function(className) {
								flatten(className, mode.keywords[className]);
							});
						}
						mode.keywords = compiled_keywords;
					}
					mode.lexemesRe = langRe(mode.lexemes || /\b\w+\b/, true);

					if (parent) {
						if (mode.beginKeywords) {
							mode.begin = '\\b(' + mode.beginKeywords.split(' ').join('|') + ')\\b';
						}
						if (!mode.begin)
							mode.begin = /\B|\b/;
						mode.beginRe = langRe(mode.begin);
						if (!mode.end && !mode.endsWithParent)
							mode.end = /\B|\b/;
						if (mode.end)
							mode.endRe = langRe(mode.end);
						mode.terminator_end = reStr(mode.end) || '';
						if (mode.endsWithParent && parent.terminator_end)
							mode.terminator_end += (mode.end ? '|' : '') + parent.terminator_end;
					}
					if (mode.illegal)
						mode.illegalRe = langRe(mode.illegal);
					if (mode.relevance === undefined)
						mode.relevance = 1;
					if (!mode.contains) {
						mode.contains = [];
					}
					var expanded_contains = [];
					mode.contains.forEach(function(c) {
						if (c.variants) {
							c.variants.forEach(function(v) {
								expanded_contains.push(inherit(c, v));
							});
						} else {
							expanded_contains.push(c == 'self' ? mode : c);
						}
					});
					mode.contains = expanded_contains;
					mode.contains.forEach(function(c) {
						compileMode(c, mode);
					});

					if (mode.starts) {
						compileMode(mode.starts, parent);
					}

					var terminators = mode.contains.map(function(c) {
						return c.beginKeywords ? '\\.?(' + c.begin + ')\\.?' : c.begin;
					}).concat([ mode.terminator_end, mode.illegal ]).map(reStr).filter(Boolean);
					mode.terminators = terminators.length ? langRe(terminators.join('|'), true) : {
						exec : function(/* s */) {
							return null;
						}
					};
				}

				compileMode(language);
			}

			/*
			 * Core highlighting function. Accepts a language name, or an alias,
			 * and a string with the code to highlight. Returns an object with
			 * the following properties:
			 *  - relevance (int) - value (an HTML string with highlighting
			 * markup)
			 * 
			 */
			function highlight(name, value, ignore_illegals, continuation) {

				function subMode(lexeme, mode) {
					for (var i = 0; i < mode.contains.length; i++) {
						if (testRe(mode.contains[i].beginRe, lexeme)) {
							return mode.contains[i];
						}
					}
				}

				function endOfMode(mode, lexeme) {
					if (testRe(mode.endRe, lexeme)) {
						while (mode.endsParent && mode.parent) {
							mode = mode.parent;
						}
						return mode;
					}
					if (mode.endsWithParent) {
						return endOfMode(mode.parent, lexeme);
					}
				}

				function isIllegal(lexeme, mode) {
					return !ignore_illegals && testRe(mode.illegalRe, lexeme);
				}

				function keywordMatch(mode, match) {
					var match_str = language.case_insensitive ? match[0].toLowerCase() : match[0];
					return mode.keywords.hasOwnProperty(match_str) && mode.keywords[match_str];
				}

				function buildSpan(classname, insideSpan, leaveOpen, noPrefix) {
					var classPrefix = noPrefix ? '' : options.classPrefix, openSpan =
							'<span class="' + classPrefix, closeSpan = leaveOpen ? '' : '</span>';

					openSpan += classname + '">';

					return openSpan + insideSpan + closeSpan;
				}

				function processKeywords() {
					if (!top.keywords)
						return escape(mode_buffer);
					var result = '';
					var last_index = 0;
					top.lexemesRe.lastIndex = 0;
					var match = top.lexemesRe.exec(mode_buffer);
					while (match) {
						result += escape(mode_buffer.substr(last_index, match.index - last_index));
						var keyword_match = keywordMatch(top, match);
						if (keyword_match) {
							relevance += keyword_match[1];
							result += buildSpan(keyword_match[0], escape(match[0]));
						} else {
							result += escape(match[0]);
						}
						last_index = top.lexemesRe.lastIndex;
						match = top.lexemesRe.exec(mode_buffer);
					}
					return result + escape(mode_buffer.substr(last_index));
				}

				function processSubLanguage() {
					var explicit = typeof top.subLanguage == 'string';
					if (explicit && !languages[top.subLanguage]) {
						return escape(mode_buffer);
					}

					var result =
							explicit ? highlight(top.subLanguage, mode_buffer, true,
								continuations[top.subLanguage]) : highlightAuto(mode_buffer,
								top.subLanguage.length ? top.subLanguage : undefined);

					// Counting embedded language score towards the host
					// language may be disabled
					// with zeroing the containing mode relevance. Usecase in
					// point is Markdown that
					// allows XML everywhere and makes every XML snippet to have
					// a much larger Markdown
					// score.
					if (top.relevance > 0) {
						relevance += result.relevance;
					}
					if (explicit) {
						continuations[top.subLanguage] = result.top;
					}
					return buildSpan(result.language, result.value, false, true);
				}

				function processBuffer() {
					return top.subLanguage !== undefined ? processSubLanguage() : processKeywords();
				}

				function startNewMode(mode, lexeme) {
					var markup = mode.className ? buildSpan(mode.className, '', true) : '';
					if (mode.returnBegin) {
						result += markup;
						mode_buffer = '';
					} else if (mode.excludeBegin) {
						result += escape(lexeme) + markup;
						mode_buffer = '';
					} else {
						result += markup;
						mode_buffer = lexeme;
					}
					top = Object.create(mode, {
						parent : {
							value : top
						}
					});
				}

				function processLexeme(buffer, lexeme) {

					mode_buffer += buffer;
					if (lexeme === undefined) {
						result += processBuffer();
						return 0;
					}

					var new_mode = subMode(lexeme, top);
					if (new_mode) {
						result += processBuffer();
						startNewMode(new_mode, lexeme);
						return new_mode.returnBegin ? 0 : lexeme.length;
					}

					var end_mode = endOfMode(top, lexeme);
					if (end_mode) {
						var origin = top;
						if (!(origin.returnEnd || origin.excludeEnd)) {
							mode_buffer += lexeme;
						}
						result += processBuffer();
						do {
							if (top.className) {
								result += '</span>';
							}
							relevance += top.relevance;
							top = top.parent;
						} while (top != end_mode.parent);
						if (origin.excludeEnd) {
							result += escape(lexeme);
						}
						mode_buffer = '';
						if (end_mode.starts) {
							startNewMode(end_mode.starts, '');
						}
						return origin.returnEnd ? 0 : lexeme.length;
					}

					if (isIllegal(lexeme, top))
						throw new Error('Illegal lexeme "' + lexeme + '" for mode "'
								+ (top.className || '<unnamed>') + '"');

					/*
					 * Parser should not reach this point as all types of
					 * lexemes should be caught earlier, but if it does due to
					 * some bug make sure it advances at least one character
					 * forward to prevent infinite looping.
					 */
					mode_buffer += lexeme;
					return lexeme.length || 1;
				}

				var language = getLanguage(name);
				if (!language) {
					throw new Error('Unknown language: "' + name + '"');
				}

				compileLanguage(language);
				var top = continuation || language;
				var continuations = {}; // keep continuations for sub-languages
				var result = '', current;
				for (current = top; current != language; current = current.parent) {
					if (current.className) {
						result = buildSpan(current.className, '', true) + result;
					}
				}
				var mode_buffer = '';
				var relevance = 0;
				try {
					var match, count, index = 0;
					while (true) {
						top.terminators.lastIndex = index;
						match = top.terminators.exec(value);
						if (!match)
							break;
						count = processLexeme(value.substr(index, match.index - index), match[0]);
						index = match.index + count;
					}
					processLexeme(value.substr(index));
					for (current = top; current.parent; current = current.parent) { // close
																					// dangling
																					// modes
						if (current.className) {
							result += '</span>';
						}
					}
					return {
						relevance : relevance,
						value : result,
						language : name,
						top : top
					};
				} catch (e) {
					if (e.message.indexOf('Illegal') != -1) {
						return {
							relevance : 0,
							value : escape(value)
						};
					} else {
						throw e;
					}
				}
			}

			/*
			 * Highlighting with language detection. Accepts a string with the
			 * code to highlight. Returns an object with the following
			 * properties:
			 *  - language (detected language) - relevance (int) - value (an
			 * HTML string with highlighting markup) - second_best (object with
			 * the same structure for second-best heuristically detected
			 * language, may be absent)
			 * 
			 */
			function highlightAuto(text, languageSubset) {
				languageSubset = languageSubset || options.languages || Object.keys(languages);
				var result = {
					relevance : 0,
					value : escape(text)
				};
				var second_best = result;
				languageSubset.forEach(function(name) {
					if (!getLanguage(name)) {
						return;
					}
					var current = highlight(name, text, false);
					current.language = name;
					if (current.relevance > second_best.relevance) {
						second_best = current;
					}
					if (current.relevance > result.relevance) {
						second_best = result;
						result = current;
					}
				});
				if (second_best.language) {
					result.second_best = second_best;
				}
				return result;
			}

			/*
			 * Post-processing of the highlighted markup:
			 *  - replace TABs with something more useful - replace real
			 * line-breaks with '<br>' for non-pre containers
			 * 
			 */
			function fixMarkup(value) {
				if (options.tabReplace) {
					value = value.replace(/^((<[^>]+>|\t)+)/gm, function(match, p1 /*
																					 * ...,
																					 * offset,
																					 * s
																					 */) {
						return p1.replace(/\t/g, options.tabReplace);
					});
				}
				if (options.useBR) {
					value = value.replace(/\n/g, '<br>');
				}
				return value;
			}

			function buildClassName(prevClassName, currentLang, resultLang) {
				var language = currentLang ? aliases[currentLang] : resultLang, result =
						[ prevClassName.trim() ];

				if (!prevClassName.match(/\bhljs\b/)) {
					result.push('hljs');
				}

				if (prevClassName.indexOf(language) === -1) {
					result.push(language);
				}

				return result.join(' ').trim();
			}

			/*
			 * Applies highlighting to a DOM node containing code. Accepts a DOM
			 * node and two optional parameters for fixMarkup.
			 */
			function highlightBlock(block) {
				var language = blockLanguage(block);
				if (isNotHighlighted(language))
					return;

				var node;
				if (options.useBR) {
					node = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
					node.innerHTML = block.innerHTML.replace(/\n/g, '').replace(/<br[ \/]*>/g, '\n');
				} else {
					node = block;
				}
				var text = node.textContent;
				var result = language ? highlight(language, text, true) : highlightAuto(text);

				var originalStream = nodeStream(node);
				if (originalStream.length) {
					var resultNode = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
					resultNode.innerHTML = result.value;
					result.value = mergeStreams(originalStream, nodeStream(resultNode), text);
				}
				result.value = fixMarkup(result.value);

				block.innerHTML = result.value;
				block.className = buildClassName(block.className, language, result.language);
				block.result = {
					language : result.language,
					re : result.relevance
				};
				if (result.second_best) {
					block.second_best = {
						language : result.second_best.language,
						re : result.second_best.relevance
					};
				}
			}

			var options = {
				classPrefix : 'hljs-',
				tabReplace : null,
				useBR : false,
				languages : undefined
			};

			/*
			 * Updates highlight.js global options with values passed in the
			 * form of an object
			 */
			function configure(user_options) {
				options = inherit(options, user_options);
			}

			/*
			 * Applies highlighting to all <pre><code>..</code></pre> blocks
			 * on a page.
			 */
			function initHighlighting() {
				if (initHighlighting.called)
					return;
				initHighlighting.called = true;

				var blocks = document.querySelectorAll('pre code');
				Array.prototype.forEach.call(blocks, highlightBlock);
			}

			/*
			 * Attaches highlighting to the page load event.
			 */
			function initHighlightingOnLoad() {
				addEventListener('DOMContentLoaded', initHighlighting, false);
				addEventListener('load', initHighlighting, false);
			}

			var languages = {};
			var aliases = {};

			function registerLanguage(name, language) {
				var lang = languages[name] = language(hljs);
				if (lang.aliases) {
					lang.aliases.forEach(function(alias) {
						aliases[alias] = name;
					});
				}
			}

			function listLanguages() {
				return Object.keys(languages);
			}

			function getLanguage(name) {
				name = name.toLowerCase();
				return languages[name] || languages[aliases[name]];
			}

			/* Interface definition */

			hljs.highlight = highlight;
			hljs.highlightAuto = highlightAuto;
			hljs.fixMarkup = fixMarkup;
			hljs.highlightBlock = highlightBlock;
			hljs.configure = configure;
			hljs.initHighlighting = initHighlighting;
			hljs.initHighlightingOnLoad = initHighlightingOnLoad;
			hljs.registerLanguage = registerLanguage;
			hljs.listLanguages = listLanguages;
			hljs.getLanguage = getLanguage;
			hljs.inherit = inherit;

			// Common regexps
			hljs.IDENT_RE = '[a-zA-Z]\\w*';
			hljs.UNDERSCORE_IDENT_RE = '[a-zA-Z_]\\w*';
			hljs.NUMBER_RE = '\\b\\d+(\\.\\d+)?';
			hljs.C_NUMBER_RE = '(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)'; // 0x...,
																										// 0...,
																										// decimal,
																										// float
			hljs.BINARY_NUMBER_RE = '\\b(0b[01]+)'; // 0b...
			hljs.RE_STARTERS_RE =
					'!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~';

			// Common modes
			hljs.BACKSLASH_ESCAPE = {
				begin : '\\\\[\\s\\S]',
				relevance : 0
			};
			hljs.APOS_STRING_MODE = {
				className : 'string',
				begin : '\'',
				end : '\'',
				illegal : '\\n',
				contains : [ hljs.BACKSLASH_ESCAPE ]
			};
			hljs.QUOTE_STRING_MODE = {
				className : 'string',
				begin : '"',
				end : '"',
				illegal : '\\n',
				contains : [ hljs.BACKSLASH_ESCAPE ]
			};
			hljs.PHRASAL_WORDS_MODE =
					{
						begin : /\b(a|an|the|are|I|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such)\b/
					};
			hljs.COMMENT = function(begin, end, inherits) {
				var mode = hljs.inherit({
					className : 'comment',
					begin : begin,
					end : end,
					contains : []
				}, inherits || {});
				mode.contains.push(hljs.PHRASAL_WORDS_MODE);
				mode.contains.push({
					className : 'doctag',
					begin : "(?:TODO|FIXME|NOTE|BUG|XXX):",
					relevance : 0
				});
				return mode;
			};
			hljs.C_LINE_COMMENT_MODE = hljs.COMMENT('//', '$');
			hljs.C_BLOCK_COMMENT_MODE = hljs.COMMENT('/\\*', '\\*/');
			hljs.HASH_COMMENT_MODE = hljs.COMMENT('#', '$');
			hljs.NUMBER_MODE = {
				className : 'number',
				begin : hljs.NUMBER_RE,
				relevance : 0
			};
			hljs.C_NUMBER_MODE = {
				className : 'number',
				begin : hljs.C_NUMBER_RE,
				relevance : 0
			};
			hljs.BINARY_NUMBER_MODE = {
				className : 'number',
				begin : hljs.BINARY_NUMBER_RE,
				relevance : 0
			};
			hljs.CSS_NUMBER_MODE =
					{
						className : 'number',
						begin : hljs.NUMBER_RE + '(' + '%|em|ex|ch|rem' + '|vw|vh|vmin|vmax'
								+ '|cm|mm|in|pt|pc|px' + '|deg|grad|rad|turn' + '|s|ms' + '|Hz|kHz'
								+ '|dpi|dpcm|dppx' + ')?',
						relevance : 0
					};
			hljs.REGEXP_MODE = {
				className : 'regexp',
				begin : /\//,
				end : /\/[gimuy]*/,
				illegal : /\n/,
				contains : [ hljs.BACKSLASH_ESCAPE, {
					begin : /\[/,
					end : /\]/,
					relevance : 0,
					contains : [ hljs.BACKSLASH_ESCAPE ]
				} ]
			};
			hljs.TITLE_MODE = {
				className : 'title',
				begin : hljs.IDENT_RE,
				relevance : 0
			};
			hljs.UNDERSCORE_TITLE_MODE = {
				className : 'title',
				begin : hljs.UNDERSCORE_IDENT_RE,
				relevance : 0
			};

			return hljs;
		}));

hljs.registerLanguage('json', function(hljs) {
	var LITERALS = {
		literal : 'true false null'
	};
	var TYPES = [ hljs.QUOTE_STRING_MODE, hljs.C_NUMBER_MODE ];
	var VALUE_CONTAINER = {
		className : 'value',
		end : ',',
		endsWithParent : true,
		excludeEnd : true,
		contains : TYPES,
		keywords : LITERALS
	};
	var OBJECT = {
		begin : '{',
		end : '}',
		contains : [ {
			className : 'attribute',
			begin : '\\s*"',
			end : '"\\s*:\\s*',
			excludeBegin : true,
			excludeEnd : true,
			contains : [ hljs.BACKSLASH_ESCAPE ],
			illegal : '\\n',
			starts : VALUE_CONTAINER
		} ],
		illegal : '\\S'
	};
	var ARRAY = {
		begin : '\\[',
		end : '\\]',
		contains : [ hljs.inherit(VALUE_CONTAINER, {
			className : null
		}) ], // inherit is also a workaround for a bug that makes shared
				// modes with endsWithParent compile only the ending of one of
				// the parents
		illegal : '\\S'
	};
	TYPES.splice(TYPES.length, 0, OBJECT, ARRAY);
	return {
		contains : TYPES,
		keywords : LITERALS,
		illegal : '\\S'
	};
});

hljs.registerLanguage('protobuf', function(hljs) {
	return {
		keywords : {
			keyword : 'package import option optional required repeated group',
			built_in : 'double float int32 int64 uint32 uint64 sint32 sint64 '
					+ 'fixed32 fixed64 sfixed32 sfixed64 bool string bytes',
			literal : 'true false'
		},
		contains : [ hljs.QUOTE_STRING_MODE, hljs.NUMBER_MODE, hljs.C_LINE_COMMENT_MODE, {
			className : 'class',
			beginKeywords : 'message enum service',
			end : /\{/,
			illegal : /\n/,
			contains : [ hljs.inherit(hljs.TITLE_MODE, {
				starts : {
					endsWithParent : true,
					excludeEnd : true
				}
			// hack: eating everything after the first title
			}) ]
		}, {
			className : 'function',
			beginKeywords : 'rpc',
			end : /;/,
			excludeEnd : true,
			keywords : 'rpc returns'
		}, {
			className : 'constant',
			begin : /^\s*[A-Z_]+/,
			end : /\s*=/,
			excludeEnd : true
		} ]
	};
});

hljs.registerLanguage('xml', function(hljs) {
	var XML_IDENT_RE = '[A-Za-z0-9\\._:-]+';
	var PHP = {
		begin : /<\?(php)?(?!\w)/,
		end : /\?>/,
		subLanguage : 'php'
	};
	var TAG_INTERNALS = {
		endsWithParent : true,
		illegal : /</,
		relevance : 0,
		contains : [ PHP, {
			className : 'attribute',
			begin : XML_IDENT_RE,
			relevance : 0
		}, {
			begin : '=',
			relevance : 0,
			contains : [ {
				className : 'value',
				contains : [ PHP ],
				variants : [ {
					begin : /"/,
					end : /"/
				}, {
					begin : /'/,
					end : /'/
				}, {
					begin : /[^\s\/>]+/
				} ]
			} ]
		} ]
	};
	return {
		aliases : [ 'html', 'xhtml', 'rss', 'atom', 'xsl', 'plist' ],
		case_insensitive : true,
		contains : [ {
			className : 'doctype',
			begin : '<!DOCTYPE',
			end : '>',
			relevance : 10,
			contains : [ {
				begin : '\\[',
				end : '\\]'
			} ]
		}, hljs.COMMENT('<!--', '-->', {
			relevance : 10
		}), {
			className : 'cdata',
			begin : '<\\!\\[CDATA\\[',
			end : '\\]\\]>',
			relevance : 10
		}, {
			className : 'tag',
			/*
			 * The lookahead pattern (?=...) ensures that 'begin' only matches '<style'
			 * as a single word, followed by a whitespace or an ending braket.
			 * The '$' is needed for the lexeme to be recognized by
			 * hljs.subMode() that tests lexemes outside the stream.
			 */
			begin : '<style(?=\\s|>|$)',
			end : '>',
			keywords : {
				title : 'style'
			},
			contains : [ TAG_INTERNALS ],
			starts : {
				end : '</style>',
				returnEnd : true,
				subLanguage : 'css'
			}
		}, {
			className : 'tag',
			// See the comment in the <style tag about the lookahead pattern
			begin : '<script(?=\\s|>|$)',
			end : '>',
			keywords : {
				title : 'script'
			},
			contains : [ TAG_INTERNALS ],
			starts : {
				end : '\<\/script\>',
				returnEnd : true,
				subLanguage : [ 'actionscript', 'javascript', 'handlebars' ]
			}
		}, PHP, {
			className : 'pi',
			begin : /<\?\w+/,
			end : /\?>/,
			relevance : 10
		}, {
			className : 'tag',
			begin : '</?',
			end : '/?>',
			contains : [ {
				className : 'title',
				begin : /[^ \/><\n\t]+/,
				relevance : 0
			}, TAG_INTERNALS ]
		} ]
	};
});
